package com.childenglish.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Comparator;  // 添加这行导入
import java.util.List;

public class FileUtils {

    private static final Logger logger = LoggerFactory.getLogger(FileUtils.class);

    // 默认分片大小 5MB
    public static final long DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024;

    // 最大分片大小 10MB
    public static final long MAX_CHUNK_SIZE = 10 * 1024 * 1024;

    /**
     * 计算文件MD5
     */
    public static String calculateFileMD5(File file) throws IOException {
        try (FileInputStream fis = new FileInputStream(file)) {
            return calculateStreamMD5(fis);
        }
    }

    /**
     * 计算输入流MD5
     */
    public static String calculateStreamMD5(InputStream inputStream) throws IOException {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }

            byte[] md5Bytes = digest.digest();
            StringBuilder sb = new StringBuilder();
            for (byte b : md5Bytes) {
                sb.append(String.format("%02x", b));
            }
            return sb.toString();

        } catch (NoSuchAlgorithmException e) {
            throw new IOException("MD5算法不支持", e);
        }
    }

    /**
     * 计算文件分片数量
     */
    public static int calculateTotalChunks(long fileSize, long chunkSize) {
        if (fileSize <= 0) return 0;
        if (chunkSize <= 0) chunkSize = DEFAULT_CHUNK_SIZE;

        return (int) Math.ceil((double) fileSize / chunkSize);
    }

    /**
     * 获取安全的文件名
     */
    public static String getSafeFileName(String originalFileName) {
        if (originalFileName == null) {
            return "unknown.dat";
        }

        // 移除路径信息，只保留文件名
        String fileName = Paths.get(originalFileName).getFileName().toString();

        // 替换非法字符
        fileName = fileName.replaceAll("[^a-zA-Z0-9.-]", "_");

        // 防止文件名过长
        if (fileName.length() > 255) {
            int dotIndex = fileName.lastIndexOf(".");
            if (dotIndex > 0) {
                String name = fileName.substring(0, Math.min(dotIndex, 200));
                String extension = fileName.substring(dotIndex);
                fileName = name + extension;
            } else {
                fileName = fileName.substring(0, 255);
            }
        }

        return fileName;
    }

    /**
     * 获取文件扩展名
     */
    public static String getFileExtension(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
    }

    /**
     * 创建目录（如果不存在）
     */
    public static void createDirectoryIfNotExists(String dirPath) throws IOException {
        Path path = Paths.get(dirPath);
        if (!Files.exists(path)) {
            Files.createDirectories(path);
            logger.info("创建目录: {}", dirPath);
        }
    }

    /**
     * 删除目录及其内容
     */
    public static boolean deleteDirectory(Path dirPath) {
        try {
            if (Files.exists(dirPath)) {
                Files.walk(dirPath)
                        .sorted(Comparator.reverseOrder())  // 这里使用Comparator
                        .map(Path::toFile)
                        .forEach(File::delete);
                logger.info("删除目录: {}", dirPath);
                return true;
            }
            return false;
        } catch (IOException e) {
            logger.error("删除目录失败: {}", dirPath, e);
            return false;
        }
    }

    /**
     * 分片文件
     */
    public static List<File> splitFile(File sourceFile, String outputDir, long chunkSize) throws IOException {
        List<File> chunkFiles = new ArrayList<>();

        if (!sourceFile.exists() || !sourceFile.isFile()) {
            throw new FileNotFoundException("源文件不存在: " + sourceFile.getPath());
        }

        if (chunkSize <= 0) {
            chunkSize = DEFAULT_CHUNK_SIZE;
        }

        createDirectoryIfNotExists(outputDir);

        try (RandomAccessFile raf = new RandomAccessFile(sourceFile, "r")) {
            long fileSize = raf.length();
            int totalChunks = calculateTotalChunks(fileSize, chunkSize);

            for (int i = 1; i <= totalChunks; i++) {
                long start = (i - 1) * chunkSize;
                long end = Math.min(start + chunkSize, fileSize);
                long chunkLength = end - start;

                String chunkFileName = String.format("%s.part%03d", sourceFile.getName(), i);
                File chunkFile = new File(outputDir, chunkFileName);

                try (RandomAccessFile chunkRaf = new RandomAccessFile(chunkFile, "rw");
                     FileOutputStream fos = new FileOutputStream(chunkFile)) {

                    raf.seek(start);
                    byte[] buffer = new byte[8192];
                    long remaining = chunkLength;

                    while (remaining > 0) {
                        int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
                        if (read == -1) break;

                        fos.write(buffer, 0, read);
                        remaining -= read;
                    }
                }

                chunkFiles.add(chunkFile);
                logger.debug("创建分片文件: {} ({} bytes)", chunkFileName, chunkLength);
            }
        }

        return chunkFiles;
    }

    /**
     * 合并分片文件
     */
    public static File mergeChunkFiles(List<File> chunkFiles, String outputFile) throws IOException {
        if (chunkFiles == null || chunkFiles.isEmpty()) {
            throw new IllegalArgumentException("分片文件列表为空");
        }

        // 按文件名排序，确保正确的合并顺序
        chunkFiles.sort(Comparator.comparing(File::getName));  // 这里使用Comparator

        File mergedFile = new File(outputFile);
        createDirectoryIfNotExists(mergedFile.getParent());

        try (FileOutputStream fos = new FileOutputStream(mergedFile)) {
            for (File chunkFile : chunkFiles) {
                if (!chunkFile.exists()) {
                    throw new FileNotFoundException("分片文件不存在: " + chunkFile.getPath());
                }

                try (FileInputStream fis = new FileInputStream(chunkFile)) {
                    byte[] buffer = new byte[8192];
                    int bytesRead;

                    while ((bytesRead = fis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                }

                logger.debug("合并分片文件: {}", chunkFile.getName());
            }
        }

        logger.info("文件合并完成: {}", outputFile);
        return mergedFile;
    }

    /**
     * 获取文件MIME类型
     */
    public static String getMimeType(File file) {
        try {
            return Files.probeContentType(file.toPath());
        } catch (IOException e) {
            logger.warn("无法探测文件MIME类型: {}", file.getName(), e);
            return "application/octet-stream";
        }
    }

    /**
     * 格式化文件大小
     */
    public static String formatFileSize(long size) {
        if (size <= 0) return "0 B";

        final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
        int digitGroups = (int) (Math.log10(size) / Math.log10(1024));

        return String.format("%.2f %s", size / Math.pow(1024, digitGroups), units[digitGroups]);
    }

    /**
     * 验证文件路径安全性
     */
    public static boolean isSafeFilePath(Path filePath, Path baseDir) {
        try {
            Path normalizedPath = filePath.normalize();
            Path normalizedBaseDir = baseDir.normalize();
            return normalizedPath.startsWith(normalizedBaseDir) &&
                    !normalizedPath.equals(normalizedBaseDir);
        } catch (Exception e) {
            logger.error("文件路径安全检查异常", e);
            return false;
        }
    }

    /**
     * 复制文件
     */
    public static void copyFile(File source, File target) throws IOException {
        createDirectoryIfNotExists(target.getParent());

        try (InputStream is = new FileInputStream(source);
             OutputStream os = new FileOutputStream(target)) {

            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        }

        logger.info("文件复制完成: {} -> {}", source.getPath(), target.getPath());
    }

    /**
     * 移动文件
     */
    public static boolean moveFile(File source, File target) {
        try {
            createDirectoryIfNotExists(target.getParent());
            return source.renameTo(target);
        } catch (Exception e) {
            logger.error("移动文件失败: {} -> {}", source.getPath(), target.getPath(), e);
            return false;
        }
    }

    /**
     * 获取目录大小
     */
    public static long getDirectorySize(Path dirPath) throws IOException {
        if (!Files.exists(dirPath)) {
            return 0;
        }

        return Files.walk(dirPath)
                .filter(path -> Files.isRegularFile(path))
                .mapToLong(path -> {
                    try {
                        return Files.size(path);
                    } catch (IOException e) {
                        logger.warn("无法获取文件大小: {}", path, e);
                        return 0;
                    }
                })
                .sum();
    }

    /**
     * 获取目录中的文件列表
     */
    public static List<File> listFiles(Path dirPath, boolean recursive) throws IOException {
        List<File> files = new ArrayList<>();

        if (!Files.exists(dirPath)) {
            return files;
        }

        if (recursive) {
            Files.walk(dirPath)
                    .filter(path -> Files.isRegularFile(path))
                    .forEach(path -> files.add(path.toFile()));
        } else {
            Files.list(dirPath)
                    .filter(path -> Files.isRegularFile(path))
                    .forEach(path -> files.add(path.toFile()));
        }

        // 按文件名排序
        files.sort(Comparator.comparing(File::getName));  // 这里使用Comparator

        return files;
    }

    /**
     * 创建临时文件
     */
    public static File createTempFile(String prefix, String suffix) throws IOException {
        Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"));
        Path tempFile = Files.createTempFile(tempDir, prefix, suffix);
        return tempFile.toFile();
    }

    /**
     * 清理临时文件
     */
    public static void cleanupTempFiles(long maxAgeMillis) {
        Path tempDir = Paths.get(System.getProperty("java.io.tmpdir"));

        try {
            Files.list(tempDir)
                    .filter(path -> {
                        try {
                            String fileName = path.getFileName().toString();
                            // 只清理我们创建的临时文件
                            if (fileName.startsWith("childenglish_") ||
                                    fileName.startsWith("upload_") ||
                                    fileName.endsWith(".tmp")) {
                                long fileAge = System.currentTimeMillis() - Files.getLastModifiedTime(path).toMillis();
                                return fileAge > maxAgeMillis;
                            }
                            return false;
                        } catch (IOException e) {
                            return false;
                        }
                    })
                    .forEach(path -> {
                        try {
                            Files.deleteIfExists(path);
                            logger.debug("清理临时文件: {}", path.getFileName());
                        } catch (IOException e) {
                            logger.warn("清理临时文件失败: {}", path.getFileName(), e);
                        }
                    });
        } catch (IOException e) {
            logger.error("清理临时文件过程发生异常", e);
        }
    }

    /**
     * 检查文件是否被占用
     */
    public static boolean isFileLocked(File file) {
        try (FileOutputStream fos = new FileOutputStream(file, true)) {
            // 如果能成功打开文件进行追加写入，说明文件没有被占用
            return false;
        } catch (IOException e) {
            return true;
        }
    }

    /**
     * 等待文件解锁
     */
    public static boolean waitForFileUnlock(File file, long timeoutMillis) {
        long startTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - startTime < timeoutMillis) {
            if (!isFileLocked(file)) {
                return true;
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }

        return false;
    }
}